package zhou.chs.p9_1;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {
    TextView tv1,tv2,tv3,tv4;  //显示读取到的寄存器值
    EditText et1,et2,et3,et4;  //编辑写入寄存器的值
    private SerialPort serialPort;      //声明串口
    private ReadThread mReadThread;    //读取线程
    private Handler myhandler;       //信息通道
    boolean running = false;
    byte[] rbuf = new byte[256];    //接收缓冲区
    byte[] tbuf = new byte[256];    //发送缓冲区
    //将待发送命令按结构初始化，发送时放入发送缓冲区，对少量数据更改后即可发送
    byte[] SD1fdl = {(byte)0x10,(byte)0x02,(byte)0x00,(byte)0x49,(byte)0x4B,(byte)0x16};
    byte[] SD1poll = {(byte)0x10,(byte)0x02,(byte)0x00,(byte)0x5C,(byte)0x5E,(byte)0x16};
    byte[] SD2read = {(byte)0x68,(byte)0x1B,(byte)0x1B,(byte)0x68,(byte)0x02,(byte)0x00,(byte)0x6C,
            (byte)0x32,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x0E,
            (byte)0x00,(byte)0x00,(byte)0x04,(byte)0x01,(byte)0x12,(byte)0x0A,(byte)0x10,(byte)0x02,
            (byte)0x00,(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x84,(byte)0x00,(byte)0x03,(byte)0x20,
            (byte)0xFF,(byte)0x16};
    byte[] SD2write = {(byte)0x68,(byte)0x21,(byte)0x21,(byte)0x68,(byte)0x02,(byte)0x00,(byte)0x6C,
            (byte)0x32,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x0E,
            (byte)0x00,(byte)0x06,(byte)0x05,(byte)0x01,(byte)0x12,(byte)0x0A,(byte)0x10,(byte)0x02,
            (byte)0x00,(byte)0x04,(byte)0x00,(byte)0x01,(byte)0x84,(byte)0x00,(byte)0x03,(byte)0xC0,
            (byte)0x00,(byte)0x04,(byte)0x00,(byte)0x20};
    int len;    //接收区字节数
    int tn=10;     //程序运行后与PLC建立连接，5秒收不到PLC数据重新连接
    boolean enw = false; //写命令使能
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv1 = (TextView)findViewById(R.id.idtv1);   //实例化控件
        tv2 = (TextView)findViewById(R.id.idtv2);
        tv3 = (TextView)findViewById(R.id.idtv3);
        tv4 = (TextView)findViewById(R.id.idtv4);
        et1 = (EditText)findViewById(R.id.idet1);
        et2 = (EditText)findViewById(R.id.idet2);
        et3 = (EditText)findViewById(R.id.idet3);
        et4 = (EditText)findViewById(R.id.idet4);
        serialPort = new SerialPort();   //创建串口
        //打开串口1，串口参数：9600,e,8,1，注意是偶校验
        serialPort.open("COM1",9600, 8, "E", 1);
        mReadThread = new ReadThread();   //声明串口接收数据线程
        mReadThread.start();    //启动串口接收数据线程
        myhandler = new MyHandler();     //实例化Handler，用于进程间的通信
        Timer mTimer = new Timer();         //新建Timer
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                tn++;                   //每秒加1
                Message msg = myhandler.obtainMessage();   //创建消息
                msg.what = 1;                      //变量what赋值
                myhandler.sendMessage(msg);     //发送消息
            }
        }, 2000, 1000);     //延时1000ms，然后每隔1000ms发送消息
    }
    //读取数据的线程
    private class ReadThread extends Thread {
        @Override
        public void run() {
            super.run();
            byte[] buff = new byte[256];
            while(true){
                try {
                    int n = serialPort.read(buff,256,100);  //接收数据
                    if(n > 0) {
                        for (int i=0;i<n;i++){
                            rbuf[i] = buff[i];    //保存数据
                        }
                        try {
                            sleep(100);     //延时100ms，等1帧数据接收完成
                        } catch (InterruptedException e) {
                        }
                        int m = serialPort.read(buff,256,100);  //接收数据
                        for (int i=0;i<m;i++){
                            rbuf[i+n] = buff[i];    //保存数据
                        }
                        Message msg = myhandler.obtainMessage();
                        msg.obj = m+n;
                        msg.what = 0;
                        myhandler.sendMessage(msg);  //收到数据，发送消息
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //在主线程处理Handler传回来的message
    class MyHandler extends Handler {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:     //收到串口数据
                    len = Integer.parseInt(msg.obj.toString());  //获取数据长度
                    if((len==1)||(rbuf[0]==(byte)0xE5)){   //收到确认帧
                        tn=1;
                        for(int i=0;i<6;i++) tbuf[i] = SD1poll[i];
                        serialPort.write(tbuf,6);   //发送查询命令
                    }
                    if((len>26)||(rbuf[19]==(byte)0x04)){  //收到读取数据
                        tv1.setText(String.format("%d", rbuf[25]));
                        tv2.setText(String.format("%d", rbuf[26]));
                        tv3.setText(String.format("%d", rbuf[27]));
                        tv4.setText(String.format("%d", rbuf[28]));
                    }
                    break;
                case 1:     //定时时间到
                    if(tn>5){
                        tn=0;
                        for(int i=0;i<6;i++) tbuf[i] = SD1fdl[i];
                        serialPort.write(tbuf,6);   //建立连接
                    }
                    if(enw){    //写入操作
                        enw=false;
                        byte[] buf =new byte[4];
                        String s = et1.getText().toString();
                        buf[0] = (byte) (Integer.parseInt(s)&0xFF);
                        s = et2.getText().toString();
                        buf[1] = (byte) (Integer.parseInt(s)&0xFF);
                        s = et3.getText().toString();
                        buf[2] = (byte) (Integer.parseInt(s)&0xFF);
                        s = et4.getText().toString();
                        buf[3] = (byte) (Integer.parseInt(s)&0xFF);
                        writePPI('V',120,buf,4); //写入VB120~VB123
                    }
                    else{
                        if(tn > 0){
                            readPPI('V',100, 4);   //读取VB100~VB103
                        }
                    }
                    break;
            }
        }
    }
    // 按钮响应程序
    public void wr(View view){
       enw=true;  //不能直接发送，在每秒发送数据里排队发送
    }
    // 生成FCS校验码
    private byte FCS(int n){
        int m = 0;
        for(int i=0;i<n;i++){
            m = m + (byte) (tbuf[i+4]&0xFF); //字节相加
        }
        return (byte)(m&0xFF); //返回低8位
    }
    // PPI读字节，area-数据区，addr-起始地址，bty-读取字节数
    private void readPPI(char area, int addr, int bty){
        for(int i=0;i<33;i++) tbuf[i] = SD2read[i];
        if(area=='I'){
            tbuf[26]=(byte)0x00;
            tbuf[27]=(byte)0x81;
        }
        if(area=='Q'){
            tbuf[26]=(byte)0x00;
            tbuf[27]=(byte)0x82;
        }
        if(area=='M'){
            tbuf[26]=(byte)0x00;
            tbuf[27]=(byte)0x83;
        }
        if(area=='V'){
            tbuf[26]=(byte)0x01;
            tbuf[27]=(byte)0x84;
        }
        addr<<=3;  //地址*8
        tbuf[28]=(byte)((addr>>16)&0xFF);
        tbuf[29]=(byte)((addr>>8)&0xFF);
        tbuf[30]=(byte)(addr&0xFF);
        tbuf[23]=(byte)((bty>>8)&0xFF);
        tbuf[24]=(byte)(bty&0xFF);
        tbuf[31]= FCS(27);  //校验
        serialPort.write(tbuf,33);   //发送读数据命令
    }
    // PPI写字节，area-数据区（只支持M区和V区），addr-起始地址，dat-待写入数据，bty-写入字节数
    private void writePPI(char area,int addr,byte[] dat,int bty){
        for(int i=0;i<35;i++) tbuf[i] = SD2write[i];
        if((area=='M')||(area=='V')) {
            if (area == 'M') {
                tbuf[26] = (byte) 0x00;
                tbuf[27] = (byte) 0x83;
            }
            if (area == 'V') {
                tbuf[26] = (byte) 0x01;
                tbuf[27] = (byte) 0x84;
            }
            addr <<= 3; //地址*8
            tbuf[28] = (byte) ((addr >> 16) & 0xFF);
            tbuf[29] = (byte) ((addr >> 8) & 0xFF);
            tbuf[30] = (byte) (addr & 0xFF);
            tbuf[23] = (byte) ((bty >> 8) & 0xFF);
            tbuf[24] = (byte) (bty & 0xFF);
            tbuf[1] = (byte) ((bty + 31) & 0xFF);
            tbuf[2] = (byte) ((bty + 31) & 0xFF);
            tbuf[16] = (byte) ((bty + 4) & 0xFF);
            int m = bty<<3; //字节数*8
            tbuf[33] = (byte)((m >> 8) & 0xFF);
            tbuf[34] = (byte)(m & 0xFF);
            for(int i=0;i<bty;i++){
                tbuf[35+i]=(byte) (dat[i]&0xFF);  //待写入数据
            }
            tbuf[35+bty] = FCS(31+bty); //校验
            tbuf[36+bty] = (byte) 0x16;
            serialPort.write(tbuf, 37+bty);   //发送读数据命令
        }
    }
    @Override   //程序退出前关闭串口
    protected void onDestroy() {
        super.onDestroy();
        serialPort.close();
    }
}
